「数据可视化库王者」D3.js 极速上手到Vue应用 |
您所在的位置:网站首页 › d3 可视化工具 › 「数据可视化库王者」D3.js 极速上手到Vue应用 |
前言 D3近年来一直是 JavaScript最重要的数据可视化库之一,在创建者 MikeBostock的维护下,前景依然无量,至少现在没有能打的: D3与众多其他库的区别在于无限定制的能力(直接操作 SVG)。它的底层 API提供对原生 SVG元素的直接控制,但它也带来了高学习曲线的成本。我们将把 D3和 Vue结合在一起 - 使用 Vue的动态数据绑定,清晰的语法和模块化结构,可以充分发挥 D3的最佳性能。![]() 根据广泛定义,D3可拆分为以下几种分库: ![]() ![]() 于此,我们不需要从 D3 DOM操作功能开始学起,直接通过实例来入门 D3。 D3.js 渐进入门以下实例的模版均为以下形式: 代码语言:javascript复制 Learn D3.js First heading 1. 选择和操作![]() 你需要学习的第一件事是如何使用D3.js选择和操作DOM元素。该库在操作DOM方面实际上非常强大,因此理论上可以将其用作 jQuery的替代品。以下代码请逐行添加运行。 代码语言:javascript复制// index.js d3.select(); d3.selectAll(); d3.select('h1').style('color', 'red') .attr('class', 'heading') .text('Updated h1 tag'); d3.select('body').append('p').text('First Paragraph'); d3.select('body').append('p').text('Second Paragraph'); d3.select('body').append('p').text('Third Paragraph'); d3.selectAll('p').style('')2.数据加载和绑定![]() 当你要创建可视化时,了解如何加载数据以及将其绑定到DOM非常重要。所以在这个实例中,你将学到这两点。 代码语言:javascript复制let dataset = [1, 2, 3, 4, 5]; d3.select('body') .selectAll('p') .data(dataset) .enter() .append('p') // appends paragraph for each data element .text('D3 is awesome!!'); //.text(function(d) { return d; });3.创建一个简单的柱状图![]() 首先需要添加一个 svg标签 代码语言:javascript复制Bar Chart using D3.js然后在 index.js中添加(已添加关键注释): 代码语言:javascript复制// 数据集 let dataset = [80, 100, 56, 120, 180, 30, 40, 120, 160]; // 定义svg图形宽高,以及柱状图间距 let svgWidth = 500, svgHeight = 300, barPadding = 5; // 通过图形计算每个柱状宽度 let barWidth = (svgWidth / dataset.length); // 绘制图形 let svg = d3.select('svg') .attr("width", svgWidth) .attr("height", svgHeight); // rect,长方形 // 文档:http://www.w3school.com.cn/svg/svg_rect.asp let barChart = svg.selectAll("rect") .data(dataset) //绑定数组 .enter() // 指定选择集的enter部分 .append("rect") // 添加足够数量的矩形 .attr("y", d => svgHeight - d ) // d为数据集每一项的值, 取y坐标 .attr("height", d => d) // 设定高度 .attr("width", barWidth - barPadding) // 设定宽度 .attr("transform", (d, i) => { let translate = [barWidth * i, 0]; return "translate("+ translate +")"; }); // 实际是计算每一项值的x坐标4. 在图形上方显示数值![]() 这时就需要在上述代码中创建 svg的 text文本 代码语言:javascript复制let text = svg.selectAll("text") .data(dataset) .enter() .append("text") .text(d => d) .attr("y", (d, i) => svgHeight - d - 2) .attr("x", (d, i) => barWidth * i) .attr("fill", "#A64C38");过程比较简单,就是返回文本,计算x/y坐标,并填充颜色。 5. scales: 比例尺函数D3中有个重要的概念就是比例尺。比例尺就是把一组输入域映射到输出域的函数。映射就是两个数据集之间元素相互对应的关系。比如输入是1,输出是100,输入是5,输出是10000,那么这其中的映射关系就是你所定义的比例尺。 D3中有各种比例尺函数,有连续性的,有非连续性的,在本例子中,你将学到 d3.scaleLinear() ,线性比例尺。 5.1 d3.scaleLinear(),线性比例尺使用 d3.scaleLinear()创造一个线性比例尺,其中: domain()是输入域range()是输出域相当于将 domain中的数据集映射到 range的数据集中。代码语言:javascript复制let scale = d3.scaleLinear().domain([1,5]).range([0,100])映射关系: ![]() 值得注意的是,上述代码只是定义了一个映射规则,映射的输入值并不局限于 domain()中的输入域。 代码语言:javascript复制scale(1) // 输出:0 scale(4) // 输出:75 scale(5) // 输出:100 scale(-1) // 输出:-50 scale(10) // 输出:225于是我们来改造 3~4的例子: 代码语言:javascript复制let dataset = [1,2,3,4,5]; let svgWidth = 500, svgHeight = 300, barPadding = 5; let barWidth = (svgWidth / dataset.length); let svg = d3.select('svg') .attr("width", svgWidth) .attr("height", svgHeight); let yScale = d3.scaleLinear() .domain([0, d3.max(dataset)]) .range([0, svgHeight]); let barChart = svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("y", d => svgHeight - yScale(d)) .attr("height", d => yScale(d)) .attr("width", barWidth - barPadding) .attr("transform", (d, i) => { let translate = [barWidth * i, 0]; return "translate("+ translate +")"; });然后就会得到以下图形: ![]() ![]() 轴是任何图表的组成部分,本例子中将会用到上面讲到的比例尺函数。 代码语言:javascript复制let data= [80, 100, 56, 120, 180, 30, 40, 120, 160]; let svgWidth = 500, svgHeight = 300; let svg = d3.select('svg') .attr("width", svgWidth) .attr("height", svgHeight); // 首先是拿最大值构建x轴坐标 let xScale = d3.scaleLinear() .domain([0, d3.max(data)]) .range([0, svgWidth]); // 接下来是反转值,用作y轴坐标。 let yScale = d3.scaleLinear() .domain([0, d3.max(data)]) .range([svgHeight, 0]); // 横轴的API使用 let x_axis = d3.axisBottom() .scale(xScale); // 纵轴的API使用 let y_axis = d3.axisLeft() .scale(yScale); // 在svg中提供了如g元素这样的将多个元素组织在一起的元素。 // 由g元素编组在一起的可以设置相同的颜色,可以进行坐标变换等,类似于Vue中的 svg.append("g") .attr("transform", "translate(50, 10)") .call(y_axis); let xAxisTranslate = svgHeight - 20; svg.append("g") .attr("transform", "translate(50, " + xAxisTranslate +")") .call(x_axis);7. 创建简易的 SVG元素![]() 在这里面,你会创建 , 和 元素 代码语言:javascript复制let svgWidth = 600, svgHeight = 500; let svg = d3.select("svg") .attr("width", svgWidth) .attr("height", svgHeight) .attr("class", "svg-container") let line = svg.append("line") .attr("x1", 100) .attr("x2", 500) .attr("y1", 50) .attr("y2", 50) .attr("stroke", "red"); let rect = svg.append("rect") .attr("x", 100) .attr("y", 100) .attr("width", 200) .attr("height", 100) .attr("fill", "#9B95FF"); let circle = svg.append("circle") .attr("cx", 200) .attr("cy", 300) .attr("r", 80) .attr("fill", "#7CE8D5");8. 创建饼图![]() ![]() 最后,你将学习如何创建折线图以显示近四个月的比特币价格。要获取数据,你将使用外部API。这个项目还将你在整个课程中学到的很多概念结合在一起,所以这是一个很好的可视化课程结束。 代码语言:javascript复制// 外部API,注意日期记得补零 const api = 'https://api.coindesk.com/v1/bpi/historical/close.json?start=2019-03-31&end=2019-07-01'; /** * dom内容加载完毕时,从API中加载数据 */ document.addEventListener("DOMContentLoaded", function(event) { fetch(api) .then(response => response.json()) .then(data => { let parsedData = parseData(data); drawChart(parsedData); }) .catch(err => console.log(err)) }); /** * 将数据解析为键值对 */ parseData = data =>{ let arr = []; for (let i in data.bpi) { arr.push({ date: new Date(i), //date value: +data.bpi[i] //convert string to number }); } return arr; } /** * 创建图表 */ drawChart = data => { let svgWidth = 600, svgHeight = 400; let margin = { top: 20, right: 20, bottom: 30, left: 50 }; let width = svgWidth - margin.left - margin.right; let height = svgHeight - margin.top - margin.bottom; let svg = d3.select('svg') .attr("width", svgWidth) .attr("height", svgHeight); let g = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); let x = d3.scaleTime() .rangeRound([0, width]); let y = d3.scaleLinear() .rangeRound([height, 0]); let line = d3.line() .x(d=> x(d.date)) .y(d=> y(d.value)) x.domain(d3.extent(data, function(d) { return d.date })); y.domain(d3.extent(data, function(d) { return d.value })); g.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)) .select(".domain") .remove(); g.append("g") .call(d3.axisLeft(y)) .append("text") .attr("fill", "#000") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .attr("text-anchor", "end") .text("Price ($)"); g.append("path") .datum(data) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-linejoin", "round") .attr("stroke-linecap", "round") .attr("stroke-width", 1.5) .attr("d", line); }以上原实例均来自:Learn D3 for free。scrimba是一个非常神奇的网站。它是使用交互式编码截屏工具构建的。 ![]() 所有的操作都是: 暂停截屏视频 → 编辑代码 → 运行它!→ 查看更改 非常值得安利一波。接下来进入第二部分: Vue中使用 D3.js的正确姿势 2. Vue中使用 D3.js的正确姿势我们将使用 D3和 Vue构建一个基本的柱状图组件。网上有一堆例子,但我们将专注于写 Vue,而不是滥用D3。 1. 安装依赖首先,我们需要为项目安装依赖项。我们可以简单地安装和使用 D3整库: 代码语言:javascript复制npm i d3但我在前面讲到,实际上 D3是几个分库的集合,考虑到项目的优化,我们只安装所需的模块。 ![]() 使用 VueCli 初始化项目即可。 2. 创建柱状图![]() ![]() ![]() 因 Vue数据响应的特性,我们不需要用到 D3操作 DOM的那套链式创建。 5. 数据与窗口大小响应![]() 在 mounted钩子中,我们将为窗口调整大小事件添加一个监听器,它将触发绘制动画,并将 大小设置为新窗口的比例。我们不会立即渲染,而是等待 300毫秒,以确保完全调整窗口大小。 以下是完整的 BarChart.vue,请配合注释食用: 代码语言:javascript复制 {{ title }} import { scaleLinear, scaleBand } from "d3-scale"; import { max, min } from "d3-array"; import { selectAll } from "d3-selection"; import { transition } from "d3-transition"; export default { name: "BarChart", props: { title: String, xKey: String, yKey: String, data: Array }, mounted() { this.svgWidth = document.getElementById("container").offsetWidth * 0.75; this.AddResizeListener(); this.AnimateLoad(); }, data: () => ({ svgWidth: 0, redrawToggle: true }), methods: { // 绘制柱形 AnimateLoad() { selectAll("rect") .data(this.data) .transition() .delay((d, i) => { return i * 150; }) .duration(1000) .attr("y", d => { return this.yScale(d[this.yKey]); }) .attr("height", d => { return this.svgHeight - this.yScale(d[this.yKey]); }); }, // 调整窗口大小后300毫秒重新绘制图表 // 即响应式绘制 AddResizeListener() { window.addEventListener("resize", () => { this.$data.redrawToggle = false; setTimeout(() => { this.$data.redrawToggle = true; this.$data.svgWidth = document.getElementById("container").offsetWidth * 0.75; this.AnimateLoad(); }, 300); }); } }, computed: { dataMax() { return max(this.data, d => { return d[this.yKey]; }); }, dataMin() { return min(this.data, d => { return d[this.yKey]; }); }, xScale() { return scaleBand() .rangeRound([0, this.svgWidth]) .padding(0.1) .domain( this.data.map(d => { return d[this.xKey]; }) ); }, // 通过线性比例尺自动生成 yScale() { return scaleLinear() .rangeRound([this.svgHeight, 0]) .domain([this.dataMin > 0 ? 0 : this.dataMin, this.dataMax]); }, svgHeight() { return this.svgWidth / 1.61803398875; // 黄金比例 } } }; .bar-positive { fill: steelblue; transition: r 0.2s ease-in-out; } .bar-positive:hover { fill: brown; } .svg-container { display: inline-block; position: relative; width: 100%; padding-bottom: 1%; vertical-align: top; overflow: hidden; }我们将从父组件 App.vue获取数据: 代码语言:javascript复制 import BarChart from "./components/BarChart.vue"; export default { name: "App", components: { BarChart }, data: () => ({ barChartData: [ { name: "张三", amount: 25 }, { name: "李四", amount: 40 }, { name: "老王", amount: 15 }, { name: "老赖", amount: 9 } ] }) }; #app { font-family: "Open Sans", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #282f36; margin-top: 30px; }这时候 yarn run serve后将会看到: ![]() 好像还缺点显示数值,考虑到该图高度是根据比例尺生成,我们调整下y坐标: 代码语言:javascript复制yScale() { return scaleLinear() .rangeRound([this.svgHeight, 0]) .domain([this.dataMin > 0 ? 0 : this.dataMin + 2, this.dataMax + 2]); },在 AnimateLoad()末尾添加: 代码语言:javascript复制selectAll("text") .data(this.data) .enter()最后在 元素中添加: 代码语言:javascript复制{{ item[xKey]}} {{ item[yKey]}}![]() ![]() 该库几乎凭 MikeBostock 一人之力完成,且在学术界、专业团队中享有极大声誉。 ![]() ![]() 掌握 D3 后,限制作品水平的只会是想象力而不再是技术。 源码地址:点这里 作者掘金文章总集需要转载到公众号的喊我加下白名单就行了。 「真®全栈之路」Web前端开发的后端指南「Vue实践」5分钟撸一个Vue CLI 插件「Vue实践」武装你的前端项目「中高级前端面试」JavaScript手写代码无敌秘籍「从源码中学习」面试官都不知道的Vue题目答案「从源码中学习」Vue源码中的JS骚操作「从源码中学习」彻底理解Vue选项Props「Vue实践」项目升级vue-cli3的正确姿势为何你始终理解不了JavaScript作用域链? |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |